import os
import time
import json
import httpx
import tempfile
from pathlib import Path
from fastapi import Request
from fastapi.responses import StreamingResponse

from app.supply import DEBUG_MODE, DATA_PATH, log


async def search_bilibili(request: Request, cookie: str):
    client = request.app.state.client
    try:
        params = dict(request.query_params)
        params.pop("cookie", None)

        headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.64",
            "accept": "application/json, text/plain, */*",
            "accept-encoding": "gzip, deflate,br",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "referer": "https://search.bilibili.com/",
            "origin": "https://search.bilibili.com",
            "sec-fetch-site": "same-site",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "cookie": cookie,
        }

        # 构造无参数的基础URL
        clean_url = "https://api.bilibili.com/x/web-interface/search/type"
        if DEBUG_MODE == True:
            log(clean_url)
            log(params)
            log(headers)
        response = await client.send(
            httpx.Request("GET", clean_url, params=params, headers=headers)
        )

        return response.json()

    except httpx.HTTPStatusError as e:
        return {"error": str(e)}, e.response.status_code
    except Exception as e:
        return {"error": str(e)}, 500


async def get_bilibili(request: Request):
    client = request.app.state.client
    try:
        params = dict(request.query_params)
        headers = {
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63",
            "accept": "*/*",
            "accept-encoding": "gzip, deflate, br",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        }

        # 构造无参数的基础URL
        clean_url = "https://api.bilibili.com/x/web-interface/view"
        if DEBUG_MODE == True:
            log(clean_url)
            log(params)
            log(headers)
        response = await client.send(
            httpx.Request("GET", clean_url, params=params, headers=headers)
        )

        # 直接透传响应头和状态码
        return response.json()

    except httpx.HTTPStatusError as e:
        return {"error": str(e)}, e.response.status_code
    except Exception as e:
        log("报错")
        return {"error": str(e)}, 500


async def bilibili_source(request: Request):
    file_path = str(DATA_PATH / "medias" / request.query_params["hash"])
    if os.path.exists(file_path + ".mp4"):
        return {"data": file_path + ".mp4"}
    elif os.path.exists(file_path + ".mp3"):
        return {"data": file_path + ".mp3"}
    else:
        client: httpx.AsyncClient = request.app.state.client
        try:
            params = dict(request.query_params)
            headers = {
                "accept": "*/*",
                "accept-encoding": "gzip, deflate, br",
                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            }

            # 构造无参数的基础URL
            clean_url = "https://api.bilibili.com/x/player/playurl"
            response = await client.send(
                httpx.Request("GET", clean_url, params=params, headers=headers)
            )
            # return response.json()
            # 直接透传响应头和状态码
            res = response.json()["data"]

            audios = res["dash"]["audio"]
            audios.sort(key=lambda x: x["bandwidth"], reverse=True)

            audio_url = get_url(audios[len(audios) // 2])
            videos = res["dash"]["video"]
            videos.sort(key=lambda x: x["bandwidth"], reverse=True)
            video_url = get_url(videos[-1])

            host_url = audio_url[audio_url.index("/") + 2 :]
            referer = list(params.values())[0]

            header = {
                "accept": "*/*",
                "accept-encoding": "gzip, deflate, br",
                "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
                "accept-charset": "*",
                "connection": "keep-alive",
                "origin": "https://www.bilibili.com",
                "host": host_url[: host_url.index("/")],
                "user-agent": request.headers.get("user-agent"),
                "referer": f"https://www.bilibili.com/video/{referer}",
            }

            video_url += f"&_t={int(time.time())}"

            return await merge_audio_video(
                client=client,
                video_url=video_url,
                audio_url=audio_url,
                header=header,
                output_path=file_path,
                cookie=request.query_params["cookie"],
            )

        except httpx.HTTPStatusError as e:
            return {"error": str(e)}, e.response.status_code
        except Exception as e:
            log(f"报错{str(e)}")
            return {"error": str(e)}, 500


def get_url(json_data):
    if json_data.get("backupUrl") is not None:
        return json_data["backupUrl"][0]
    else:
        return json_data["baseUrl"]


async def bilibili_stream(request: Request, url: str):
    log("bilibili_stream")
    client = request.app.state.client
    # merge_audio_video(client=client, video_url=url, audio_url=url, output_path="output.mp4")
    try:
        # 添加必要请求头
        headers = {
            "referer": "https://www.bilibili.com",
            "user-agent": request.headers.get("user-agent"),
            "range": request.headers.get("range", ""),  # 透传范围请求头
        }

        # 发送 HEAD 请求获取文件信息
        head_resp = await client.head(url, headers=headers)
        content_length = int(head_resp.headers.get("content-length", 0))
        content_type = head_resp.headers.get("content-type", "video/mp4")
        accept_ranges = head_resp.headers.get("accept-ranges", "bytes")

        # 处理范围请求
        range_header = request.headers.get("range")
        status_code = 200
        start = 0
        end = content_length - 1

        if range_header:
            # 解析范围请求
            bytes_range = range_header.replace("bytes=", "").split("-")
            start = int(bytes_range[0])
            end = int(bytes_range[1]) if bytes_range[1] else content_length - 1
            status_code = 206

            # 设置范围请求头
            headers["range"] = f"bytes={start}-{end}"

        # 流式响应生成器
        async def stream_generator():
            async with client.stream("GET", url, headers=headers) as response:
                remaining = end - start + 1
                async for chunk in response.aiter_bytes():
                    if remaining <= 0:
                        break
                    yield chunk[:remaining]
                    remaining -= len(chunk)

        # 构建响应头
        response_headers = {
            "accept-ranges": accept_ranges,
            "content-type": content_type,
            "content-length": str(end - start + 1),
            "content-range": f"bytes {start}-{end}/{content_length}",
        }

        return StreamingResponse(
            content=stream_generator(),
            status_code=status_code,
            headers=response_headers,
            media_type=content_type,
        )

    except httpx.HTTPStatusError as e:
        return {"error": str(e)}, e.response.status_code
    except Exception as e:
        return {"error": str(e)}, 500


async def merge_audio_video(
    client, video_url: str, audio_url: str, header: dict, output_path: str, cookie: str
):
    try:
        with tempfile.TemporaryDirectory() as tmpdir:
            # 下载文件
            audio_path = output_path + ".mp3"
            await download_file(client, audio_url, audio_path, header, cookie)
            if os.path.exists(audio_path):
                return {"data": audio_path}
            else:
                log(f"{output_path} 下载失败")
            # video_path = os.path.join(tmpdir, "video.mp4")
            # audio_path = os.path.join(tmpdir, "audio.mp3")

            # video_task = asyncio.create_task(download_file(client, video_url, video_path, header, cookie))
            # audio_task = asyncio.create_task(download_file(client, audio_url, audio_path, header, cookie))
            # await asyncio.gather(video_task, audio_task)

            # if  os.path.exists(video_path) & os.path.exists(audio_path):
            #     ffmpeg_cmd = [
            #             'ffmpeg',
            #             '-y',  # 覆盖输出文件
            #             '-i', video_path,
            #             '-i', audio_path,
            #             '-c:v', 'copy',     # 直接复制视频流
            #             '-c:a', 'aac',      # 转码音频为AAC格式
            #             '-shortest',        # 以较短的媒体时长为准
            #             file_path
            #         ]

            #     subprocess.run(ffmpeg_cmd, check=True, capture_output=True)
            #     log(f"{file_path} 转码完成")
            #     return {'data': file_path}
            # else :
            #     if os.path.exists(audio_path):
            #         path = str(DATA_PATH / 'medias' / "audio.mp3")
            #         shutil.copy(audio_path, path)
            #         log(f"{path} 下载歌曲")
            #         return {'data': path}
            #     else:
            #         log(f"{file_path} 转码失败")

    except httpx.HTTPStatusError as e:
        log(f"HTTP error occurred: {e}")
    except Exception as e:
        log(f"报错: {e}")


async def download_file(
    client: httpx.AsyncClient, url: str, path: Path, header: dict, cookie: str
):
    # log(f"打印url和header {url} to {header}")
    try:
        async with client.stream(
            "GET", url, headers=header, cookies=json.loads(cookie)
        ) as response:
            response.raise_for_status()
            with open(path, "wb") as f:
                async for chunk in response.aiter_bytes(chunk_size=8192):
                    f.write(chunk)
    except httpx.HTTPStatusError as e:
        log(f"HTTP error occurred: {e}")
    except Exception as e:
        log(f"报错: {e}")
